#include "Rubik.h"
#include "ansicolor.h"
#include "engine.h"

int mygetch( ) {
    struct termios oldt,
    newt;
    int ch;
    tcgetattr( STDIN_FILENO, &oldt );
    newt = oldt;
    newt.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN_FILENO, TCSANOW, &newt );
    ch = getchar();
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
    return ch;
}

void CopyRubik(Rubik &src, Rubik &dest)
{
	for(int i=0; i<6; i++)
	{
		dest.center[i]=src.center[i];
		for(int j=0;j<4;j++)
		{
			dest.edge[i][j]=src.edge[i][j];
			dest.corner[i][j]=src.corner[i][j];
		}
	}
}
int FindBack(int face1, int ID_cNum)
{
	int face2=Relation[face1][ID_cNum];
	int result=0;
	for(int i=0;i<5;i++)
	{
		if(Relation[face2][i]==face1)
		{	result=i; break; }
	}
	return result;
}

void Swap4(int &Int0 ,int &Int1,int &Int2, int &Int3, bool oppo)
{
	int swaptemp=0;

	if(!oppo)
	{
		swaptemp=Int3;
		Int3=Int2;
		Int2=Int1;
		Int1=Int0;
		Int0=swaptemp;
	}
	else
	{
		swaptemp=Int0;
		Int0=Int1;
		Int1=Int2;
		Int2=Int3;
		Int3=swaptemp;
	}
}
void Turn(Rubik &rbk,int face, bool oppo)
{
	int *pEdge[4];
	int *pCornerR[4];
	int *pCornerL[4];
	for(int i=0;i<4;i++)
	{
		int thatface=Relation[face][i];
		int corr=FindBack(face,i);
		pEdge[i]=&rbk.edge[thatface][corr];  //?
		pCornerR[i]=&rbk.corner[thatface][EdgeCornerR[corr]];  //?
		pCornerL[i]=&rbk.corner[thatface][EdgeCornerL[corr]];
	}

	Swap4(rbk.edge[face][0],rbk.edge[face][1],rbk.edge[face][2],rbk.edge[face][3],oppo);
	Swap4(rbk.corner[face][0],rbk.corner[face][1],rbk.corner[face][2],rbk.corner[face][3],oppo);
	Swap4(*pEdge[0],*pEdge[1],*pEdge[2],*pEdge[3],oppo);
	Swap4(*pCornerR[0],*pCornerR[1],*pCornerR[2],*pCornerR[3],oppo);
	Swap4(*pCornerL[0],*pCornerL[1],*pCornerL[2],*pCornerL[3],oppo);
	
}
void TurnCenter(Rubik &rbk,int face, bool oppo)
{
	int *pEdge[4];
	int *pCenter[4];
	int *pEdgeR[4];
	int *pEdgeL[4];
	for(int i=0;i<4;i++)
	{
		int thatface=Relation[face][i];
		int corr=FindBack(face,i);
		pCenter[i]=&rbk.center[thatface];
		pEdge[i]=&rbk.edge[thatface][corr];
		pEdgeR[i]=&rbk.edge[thatface][EdgeEdgeR[corr]];
		pEdgeL[i]=&rbk.edge[thatface][EdgeEdgeL[corr]];
	}
	Swap4(*pCenter[0],*pCenter[1],*pCenter[2],*pCenter[3],oppo);
	Swap4(*pEdgeR[0],*pEdgeR[1], *pEdgeR[2], *pEdgeR[3],oppo);
	Swap4(*pEdgeL[0],*pEdgeL[1], *pEdgeL[2], *pEdgeL[3],oppo);
}
void TurnTotal(Rubik &rbk, int face, bool oppo)
{
	Turn(rbk,face, oppo);
	Turn(rbk,Relation[face][ID_oppo],!oppo);
	TurnCenter(rbk, face, oppo);
}
int TranslateColor(char inchar)
{
	int result=cK;
	switch (inchar)
	{
	case 'R':
	case 'r':
		result=cR; break;
	case 'G':
	case 'g':
		result=cG; break;
	case 'B':
	case 'b':
		result=cB; break;
	case 'W':
	case 'w':
		result=cW; break;
	case 'Y':
	case 'y':
		result=cY; break;
	case 'O':
	case 'o':
		result=cO; break;
	default:
		result=cK;
	}
	return result;
}
char TranslateToColor(int color)
{
	char result;
	switch(color)
	{
	case cR:
		result='R' ;break;
	case cG:
		result='G' ;break;
	case cB:
		result='B' ;break;
	case cY:
		result='Y' ;break;
	case cW:
		result='W' ;break;
	case cO:
		result='O' ;break;
	default:
		result='X' ;break;
	}
	return result;
}
void ManuallySetRubik(Rubik &rubik, bool init)
{
	char inputChar;
	string faceNames[6]={"Up","Down", "Front","Back","Left","Right"};
	for(int i=0;i<6;i++)
	{
		cout<<"Input Center Color of"+faceNames[i]+" :"<<endl;
		cin>>inputChar;
		rubik.center[i]=TranslateColor(inputChar);
	}
	if(init)
	{
		for(int i=0;i<6;i++)
		{
			for(int j=0;j<4;j++)
			{
				rubik.corner[i][j]=rubik.center[i];
				rubik.edge[i][j]=rubik.center[i];
			}
		}
	}
}

int* RubikFaceIndex(Rubik &rbk,int face, int index)
{
	int* result=&rbk.center[0];
	switch(index)
	{
	case 0:
		result=&rbk.corner[face][3];break;
	case 1:
		result=&rbk.edge[face][0];break;
	case 2:
		result=&rbk.corner[face][0];break;
	case 3:
		result=&rbk.edge[face][3];break;
	case 4:
		result=&rbk.center[face];break;
	case 5:
		result=&rbk.edge[face][1];break;
	case 6:
		result=&rbk.corner[face][2];break;
	case 7:
		result=&rbk.edge[face][2];break;
	case 8:
		result=&rbk.corner[face][1];break;
	default:
		result=&rbk.center[face];
	}
	return result;
}


string RubikToString(Rubik &rbk)
{
	char r[55];
	r[54]='\0';
	for(int i=0;i<6;i++)
	{
		for(int j=0;j<9;j++)
		{
			r[i*9+j]=TranslateToColor(*RubikFaceIndex(rbk,i,j));
		}
	}
	string result=r;
	return result;
}

bool StringToRubik(Rubik &rbk,string posstr)
{
	if(posstr.length()==54)
	{
		
		for(int i=0;i<6;i++)
		{
			for(int j=0;j<9;j++)
			{
				*RubikFaceIndex(rbk,i,j)=TranslateColor(posstr[i*9+j]);
			}
		}
		return true;
	}
	return false;
}

bool TranslateMove(Rubik &rbk,char ch)
{
	char newch;
	string toExec="";
	//cout<<ch<<endl;
	switch(ch)
	{
	case 'l' :
		Turn(rbk,L , 0); break;
	case 'L' :
		Turn(rbk,L , 1); break;
	case 'r' :
		Turn(rbk,R ,0 ); break;
	case 'R' :
		Turn(rbk, R, 1); break;
	case 'f' :
		Turn(rbk,F, 0); break;
	case 'F' :
		Turn(rbk, F, 1); break;
	case 'b' :
		Turn(rbk, B, 0); break;
	case 'B' :
		Turn(rbk, B, 1); break;
	case 'u' :
		Turn(rbk, U, 0); break;
	case 'U' :
		Turn(rbk, U, 1); break;
	case 'd' :
		Turn(rbk, D, 0); break;
	case 'D' :
		Turn(rbk, D, 1); break;
	case '6' :
		TurnTotal(rbk, U, 1); break;
	case '4' :
		TurnTotal(rbk, U); break;
	case '8' :
		TurnTotal(rbk, R); break;
    case '2' :
        TurnTotal(rbk, R, 1); break;
	case '5' :
        TurnTotal(rbk, F); break;
	case '0' :
		TurnTotal(rbk, F, 1); break;
	case '*' :
		cout<<"Reset the rubik? (y/n)"<<endl;
		newch= mygetch();
		while(newch!='y' && newch!='n' && newch!='Y' && newch!='N')
			newch=mygetch();
		if(newch=='Y' || newch=='y')
		  	StringToRubik(rbk,"wwwwwwwwwyyyyyyyyyrrrrrrrrrooooooooobbbbbbbbbggggggggg");
		break;
	case '-' :
		cout<<"Exit? (y/n)"<<endl;
        newch= mygetch();
        while(newch!='y' && newch!='n' && newch!='Y' && newch!='N')
	         newch=mygetch();
		if(newch=='Y' || newch=='y')
			exit(0);
		break;
	case '/' :
		cout<<"Random (r), Solve (s),  Print (p),  Load (l),  Execute (e)"<<endl;
		newch= mygetch();
		switch(newch)
		{
			case 'r':
			case 'R':
				RandomRubik(rbk) ;break;
			case 'p':
			case 'P':
				PrintRubikStr(rbk);break;
			case 'l':
			case 'L':
				SetRubikWithString(rbk);break;
			case 'e':
			case 'E':
				cout<<"Input Moves"<<endl;
				cin>>toExec;
				TurnRubikString(rbk, toExec);
				break;
			case 's':
			case 'S':
				SolveRubik(rbk);
				break;
		}
		break;
	case '?':
		OnHelp(); break;
	}
	return true;
}

bool TurnRubikString(Rubik &rbk, string moves)
{
	    //if moves has something other than uUdD.... return false
		//    //else move it and return true
        for(int i=0;i<moves.length();i++)
        {
             TranslateMove(rbk, moves[i]);
        }          
       return true;
}           

void RandomRubik(Rubik &rbk)
{
	//char names[12]={ 'u','U','d','D','l','L','r','R','f','F','b','B'};
	char ch;
	srand((unsigned)time(NULL));
	for(int i=0;i<50;i++)
	{
		ch=MoveNames[(rand()%12)];
		TranslateMove(rbk, ch);
		system("clear");
		DrawRubik(rbk);
	}
}
void PrintRubikStr(Rubik &rbk)
{
	cout<<RubikToString(rbk)<<endl;
}
void SetRubikWithString(Rubik &rbk)
{
	string str;
	cout<<"Input the position string: "<<endl;
	cin>>str;
	bool suc=StringToRubik(rbk, str);
	system("clear");
	DrawRubik(rbk);
	if (suc)
		cout<<"Position Set~~"<<endl;
	else
		cout<<"Wrong position"<<endl;
}
void OnHelp()
{
	cout<<"Press ( u U d D r R l L f F b B ) to turn the correspondent side"<<endl;
	cout<<"Press the NumPad ( 4 6 8 2 5 0 ) to turn the whole Rubik"<<endl;
	cout<<"Press - to quit, Press * to reset, Press ? for help"<<endl;
	cout<<"Press / to randomize the Rubik, printing and loading the position, etc"<<endl;
	cout<<endl;
	cout<<"\033[1;32mDodo Rubik\033[0m   -- by \033[1;33mclaydodo\033[0m"<<endl;
}

